
/*    This file is part of the managarm operating system.
 *   Copyright (C) 2007, 2008, 2009  Alexander van der Grinten
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

package org.managarm.jmanaintlib.runtime.system;

import java.util.TreeMap;

import org.managarm.jmanaintlib.machine.VmMachine;
import org.managarm.jmanaintlib.runtime.functions.FuncRefGetIdentity;
import org.managarm.jmanaintlib.runtime.functions.FuncChanAtEnd;
import org.managarm.jmanaintlib.runtime.functions.FuncChanOpen;
import org.managarm.jmanaintlib.runtime.functions.FuncChanRead;
import org.managarm.jmanaintlib.runtime.functions.FuncChanWrite;
import org.managarm.jmanaintlib.runtime.functions.FuncChanClose;
import org.managarm.jmanaintlib.runtime.functions.FuncFileGetChild;
import org.managarm.jmanaintlib.runtime.functions.FuncFileGetParent;
import org.managarm.jmanaintlib.runtime.functions.FuncFileIdent;
import org.managarm.jmanaintlib.runtime.functions.FuncFileNodeType;
import org.managarm.jmanaintlib.runtime.functions.FuncFileOpen;
import org.managarm.jmanaintlib.runtime.functions.FuncFileSize;
import org.managarm.jmanaintlib.runtime.functions.FuncThreadCurrent;
import org.managarm.jmanaintlib.runtime.functions.FuncThreadCreate;
import org.managarm.jmanaintlib.runtime.functions.FuncThreadEnterState;
import org.managarm.jmanaintlib.runtime.functions.FuncProcessCurrent;
import org.managarm.jmanaintlib.runtime.functions.FuncProcessGetConfig;
import org.managarm.jmanaintlib.runtime.functions.FuncProcessSetConfig;
import org.managarm.jmanavalib.core.VaClsInfo;
import org.managarm.jmanavalib.core.VaDatatype;
import org.managarm.jmanavalib.core.VaMthInfo;
import org.managarm.jmanavalib.utils.VaImage;

public class RtInstance {
	private ThreadLocal<Long> currentThread = new ThreadLocal<Long>();
	private final TreeMap<Long, Object> objectMap
			= new TreeMap<Long, Object>();
	private long lastObjectId = 0;

/* ---------------- METHODS CALLED BY THE OBJECT CREATOR -------------*/
	public RtInstance() {
		
	}
	
	public void initialize() {
		
	}
	
	public void finish() {
		
	}
	
	public long makeProcess(VaImage image) {
		// create a class desc for the runtime
		final VaClsInfo objectcls
				= new VaClsInfo("Korona.Object", 0, 0, 0, 0);
		final VaClsInfo rtcls
				= new VaClsInfo("Korona.RtPrimitives", 0, 0, 0, 0);
		
		// create argument descriptions for our methods
		final VaDatatype[] rtargs_ref_get_identity = {
				new VaDatatype.DtClass(objectcls) };
		final VaDatatype[] rtargs_file_open = {
				new VaDatatype.DtArray(new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_CHAR)) };
		final VaDatatype[] rtargs_file_ident = { new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT) };
		final VaDatatype[] rtargs_file_size = { new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT) };
		final VaDatatype[] rtargs_file_node_type = { new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT) };
		final VaDatatype[] rtargs_file_get_parent = { new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT) };
		final VaDatatype[] rtargs_file_get_child = { new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT), new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT) };
		final VaDatatype[] rtargs_process_current = { };
		final VaDatatype[] rtargs_process_get_config = {
				new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UINT),
				new VaDatatype.DtArray(new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_CHAR)) };
		final VaDatatype[] rtargs_process_set_config = {
				new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UINT),
				new VaDatatype.DtArray(new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_CHAR)),
				new VaDatatype.DtArray(new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_CHAR)) };
		final VaDatatype[] rtargs_thread_current = { };
		final VaDatatype[] dgargs_thread_create = { };
		final VaDatatype[] rtargs_thread_create = { new VaDatatype.DtDelegate
				(new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_NULL),
				dgargs_thread_create ) };
		final VaDatatype[] rtargs_thread_enter_state = {
				new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UINT),
				new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UINT),
				new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UINT) };
		final VaDatatype[] rtargs_chan_open = {
				new VaDatatype.DtArray(new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_CHAR)) };
		final VaDatatype[] rtargs_chan_write = { new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT), new VaDatatype.DtArray
				(new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UBYTE)),
				new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UINT),
				new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UINT) };
		final VaDatatype[] rtargs_chan_read = { new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT), new VaDatatype.DtArray
				(new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UBYTE)),
				new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UINT),
				new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UINT) };
		final VaDatatype[] rtargs_chan_at_end = { new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT) };
		final VaDatatype[] rtargs_chan_close = { new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT) };
		
		// create descriptions for the runtime methods
		final VaMthInfo rtdesc_ref_get_identity = new VaMthInfo("refGetIdentity", rtcls,
				new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT), rtargs_ref_get_identity,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_file_open = new VaMthInfo("fileOpen", rtcls,
				new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT), rtargs_file_open,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_file_ident = new VaMthInfo("fileIdent", rtcls,
				new VaDatatype.DtArray(new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_CHAR)), rtargs_file_ident,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_file_size = new VaMthInfo("fileSize", rtcls,
				new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT), rtargs_file_size,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_file_node_type = new VaMthInfo("fileNodeType",
				rtcls, new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT), rtargs_file_node_type,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_file_get_parent = new VaMthInfo("fileGetParent",
				rtcls, new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT), rtargs_file_get_parent,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_file_get_child = new VaMthInfo("fileGetChild",
				rtcls, new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT), rtargs_file_get_child,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_process_current = new VaMthInfo
				("processCurrent", rtcls, new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT), rtargs_process_current,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_process_get_config = new VaMthInfo
				("processGetConfig", rtcls, new VaDatatype.DtArray
				(new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_CHAR)),
				rtargs_process_get_config,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_process_set_config = new VaMthInfo
				("processSetConfig", rtcls, new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_NULL), rtargs_process_set_config,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_thread_current = new VaMthInfo("threadCurrent",
				rtcls, new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT), rtargs_thread_current,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_thread_create = new VaMthInfo("threadCreate",
				rtcls, new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT), rtargs_thread_create,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_thread_enter_state = new VaMthInfo("threadEnterState",
				rtcls, new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_NULL), rtargs_thread_enter_state,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_chan_open = new VaMthInfo("chanOpen", rtcls,
				new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT), rtargs_chan_open,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_chan_write = new VaMthInfo("chanWrite", rtcls,
				new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_NULL), rtargs_chan_write,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_chan_read = new VaMthInfo("chanRead", rtcls,
				new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_NULL), rtargs_chan_read,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_chan_at_end = new VaMthInfo("chanAtEnd", rtcls,
				new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_BOOLEAN), rtargs_chan_at_end,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);
		final VaMthInfo rtdesc_chan_close = new VaMthInfo("chanClose", rtcls,
				new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_NULL), rtargs_chan_close,
				VaMthInfo.FLAG0_NATIVE | VaMthInfo.FLAG0_STATIC);

		// setup the virtual machine
		final VmMachine machine = new VmMachine(image);
		machine.setupCode(new FuncRefGetIdentity(this, rtdesc_ref_get_identity));
		machine.setupCode(new FuncFileOpen(this, rtdesc_file_open));
		machine.setupCode(new FuncFileIdent(this, rtdesc_file_ident));
		machine.setupCode(new FuncFileSize(this, rtdesc_file_size));
		machine.setupCode(new FuncFileNodeType(this, rtdesc_file_node_type));
		machine.setupCode(new FuncFileGetParent(this, rtdesc_file_get_parent));
		machine.setupCode(new FuncFileGetChild(this, rtdesc_file_get_child));
		machine.setupCode(new FuncProcessCurrent(this, rtdesc_process_current));
		machine.setupCode(new FuncProcessGetConfig
				(this, rtdesc_process_get_config));
		machine.setupCode(new FuncProcessSetConfig
				(this, rtdesc_process_set_config));
		machine.setupCode(new FuncThreadCurrent(this, rtdesc_thread_current));
		machine.setupCode(new FuncThreadCreate(this, rtdesc_thread_create));
		machine.setupCode(new FuncThreadEnterState(this, rtdesc_thread_enter_state));
		machine.setupCode(new FuncChanOpen(this, rtdesc_chan_open));
		machine.setupCode(new FuncChanWrite(this, rtdesc_chan_write));
		machine.setupCode(new FuncChanRead(this, rtdesc_chan_read));
		machine.setupCode(new FuncChanAtEnd(this, rtdesc_chan_at_end));
		machine.setupCode(new FuncChanClose(this, rtdesc_chan_close));
		
		return objectRegister(new RtProcess(machine));
	}
	
	public void executeThread(long process,
			String clsident, String mthident) {
		// create class and method description structs
		final VaDatatype[] argtypes = { };
		final VaClsInfo clsinfo = new VaClsInfo(clsident, 0, 0, 0, 0);
		final VaMthInfo mthinfo = new VaMthInfo(mthident,
				clsinfo, new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_NULL), argtypes,
				VaMthInfo.FLAG0_STATIC);
		
		// create a thread and let it setup itself
		final RtThread thread = new RtThread(this, process, mthinfo, null);
		final long idnumber = objectRegister(thread);
		thread.execute(idnumber);
		
		thread.enterState(RtThread.STATE_ACTIVE);
		while(thread.currentState() != RtThread.STATE_FINISHED)
			Thread.yield(); // wait until the thread finished its work
	}
	
	public void setupConfig(long handle, String variable, String string) {
		final RtProcess process = (RtProcess)objectRequest(handle);
		process.setConfig(variable, string);
	}
	
/* ---------- METHODS CALLED BY RUNTIME FUNCTIONS -------------*/
	public void setCurrentThread(long threadid) {
		currentThread.set(threadid);
	}
	
	public long currentThread() {
		return currentThread.get();
	}
	
	public Object objectRequest(long channelid) {
		return objectMap.get(channelid);
	}
	public long objectRegister(Object object) {
		lastObjectId++;
		final long channelid = lastObjectId;
		objectMap.put(channelid, object);
		return channelid;
	}
}
